home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume15 / cshar / part03 < prev    next >
Encoding:
Internet Message Format  |  1988-05-26  |  26.1 KB

  1. Subject:  v15i020:  Tools to create and unpack shell archives, Part03/03
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4.  
  5. Submitted-by: Rich Salz <rsalz@bbn.com>
  6. Posting-number: Volume 15, Issue 20
  7. Archive-name: cshar/part03
  8.  
  9. #! /bin/sh
  10. # This is a shell archive.  Remove anything before this line, then unpack
  11. # it by saving it into a file and typing "sh file".  To overwrite existing
  12. # files, type "sh file -c".  You can also feed this as standard input via
  13. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  14. # will see the following message at the end:
  15. #        "End of archive 3 (of 3)."
  16. # Contents:  parser.c
  17. # Wrapped by rsalz@fig.bbn.com on Fri May 27 14:15:09 1988
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. if test -f 'parser.c' -a "${1}" != "-c" ; then 
  20.   echo shar: Will not clobber existing file \"'parser.c'\"
  21. else
  22. echo shar: Extracting \"'parser.c'\" \(24206 characters\)
  23. sed "s/^X//" >'parser.c' <<'END_OF_FILE'
  24. X/*
  25. X**  An interpreter that can unpack many /bin/sh shell archives.
  26. X**  This program should really be split up into a couple of smaller
  27. X**  files; it started with Argify and SynTable as a cute ten-minute
  28. X**  hack and it just grew.
  29. X**
  30. X**  Also, note that (void) casts abound, and that every command goes
  31. X**  to some trouble to return a value.  That's because I decided
  32. X**  not to implement $? "properly."
  33. X*/
  34. X#include "shar.h"
  35. X#ifdef    RCSID
  36. static char RCS[] =
  37. X    "$Header: parser.c,v 2.0 88/05/27 13:27:39 rsalz Exp $";
  38. X#endif    /* RCSID */
  39. X
  40. X
  41. X/*
  42. X**  Manifest constants, handy shorthands.
  43. X*/
  44. X
  45. X/* Character classes used in the syntax table. */
  46. X#define C_LETR        1        /* A letter within a word    */
  47. X#define C_WHIT        2        /* Whitespace to separate words    */
  48. X#define C_WORD        3        /* A single-character word    */
  49. X#define C_DUBL        4        /* Something like <<, e.g.    */
  50. X#define C_QUOT        5        /* Quotes to group a word    */
  51. X#define C_META        6        /* Heavy magic character    */
  52. X#define C_TERM        7        /* Line terminator        */
  53. X
  54. X/* Macros used to query character class. */
  55. X#define ISletr(c)    (SynTable[(c)] == C_LETR)
  56. X#define ISwhit(c)    (SynTable[(c)] == C_WHIT)
  57. X#define ISword(c)    (SynTable[(c)] == C_WORD)
  58. X#define ISdubl(c)    (SynTable[(c)] == C_DUBL)
  59. X#define ISquot(c)    (SynTable[(c)] == C_QUOT)
  60. X#define ISmeta(c)    (SynTable[(c)] == C_META)
  61. X#define ISterm(c)    (SynTable[(c)] == C_TERM)
  62. X
  63. X
  64. X/*
  65. X**  Data types
  66. X*/
  67. X
  68. X/* Command dispatch table. */
  69. typedef struct {
  70. X    char      Name[10];        /* Text of command name        */
  71. X    int        (*Func)();        /* Function that implements it    */
  72. X} COMTAB;
  73. X
  74. X/* A shell variable.  We only have a few of these. */
  75. typedef struct {
  76. X    char     *Name;
  77. X    char     *Value;
  78. X} VAR;
  79. X
  80. X
  81. X/*
  82. X**  Global variables.
  83. X*/
  84. X
  85. XFILE        *Input;            /* Current input stream        */
  86. char        *File;            /* Input filename        */
  87. int         Interactive;        /* isatty(fileno(stdin))?    */
  88. X#ifdef    MSDOS
  89. jmp_buf         jEnv;            /* Pop out of main loop        */
  90. X#endif    MSDOS
  91. X
  92. extern COMTAB     Dispatch[];        /* Defined below...        */
  93. static VAR     VarList[MAX_VARS];    /* Our list of variables    */
  94. static char     Text[BUFSIZ];        /* Current text line        */
  95. static int     LineNum = 1;        /* Current line number        */
  96. static int     Running = TRUE;    /* Working, or skipping?    */
  97. static short     SynTable[256] = {    /* Syntax table            */
  98. X    /*    \0    001    002    003    004    005    006    007    */
  99. X    C_TERM,    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,
  100. X    /*    \h    \t    \n    013    \f    \r    016    017    */
  101. X    C_WHIT,    C_WHIT,    C_TERM,    C_WHIT,    C_TERM,    C_TERM,    C_WHIT,    C_WHIT,
  102. X    /*    020    021    022    023    024    025    026    027    */
  103. X    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,
  104. X    /*    can    em    sub    esc    fs    gs    rs    us    */
  105. X    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,    C_WHIT,
  106. X
  107. X    /*    sp    !    "    #    $    %    &    '    */
  108. X    C_WHIT,    C_LETR,    C_QUOT,    C_TERM,    C_LETR,    C_LETR,    C_DUBL,    C_QUOT,
  109. X    /*    (    )    *    +    ,    -    .    /    */
  110. X    C_WORD,    C_WORD,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,
  111. X    /*    0    1    2    3    4    5    6    7    */
  112. X    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,
  113. X    /*    8    9    :    ;    <    =    >    ?    */
  114. X    C_LETR,    C_LETR,    C_LETR,    C_DUBL,    C_DUBL,    C_LETR,    C_DUBL,    C_LETR,
  115. X
  116. X    /*    @    A    B    C    D    E    F    G    */
  117. X    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,
  118. X    /*    H    I    J    K    L    M    N    O    */
  119. X    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,
  120. X    /*    P    Q    R    S    T    U    V    W    */
  121. X    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,
  122. X    /*    X    Y    Z    [    \    ]    ^    _    */
  123. X    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_META,    C_LETR,    C_LETR,    C_LETR,
  124. X
  125. X    /*    `    a    b    c    d    e    f    g    */
  126. X    C_WORD,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,
  127. X    /*    h    i    j    k    l    m    n    o    */
  128. X    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,
  129. X    /*    p    q    r    s    t    u    v    w    */
  130. X    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_LETR,
  131. X    /*    x    y    z    {    |    }    ~    del    */
  132. X    C_LETR,    C_LETR,    C_LETR,    C_LETR,    C_DUBL,    C_LETR,    C_LETR,    C_WHIT,
  133. X};
  134. X
  135. X/**
  136. X***        E R R O R   R O U T I N E S
  137. X**/
  138. X
  139. X
  140. X/*
  141. X**  Print message with current line and line number.
  142. X*/
  143. static void
  144. Note(text, arg)
  145. X    char    *text;
  146. X    char    *arg;
  147. X{
  148. X    Fprintf(stderr, "\nIn line %d of %s:\n\t", LineNum, File);
  149. X    Fprintf(stderr, text, arg);
  150. X    Fprintf(stderr, "Current line:\n\t%s\n", Text);
  151. X    (void)fflush(stderr);
  152. X}
  153. X
  154. X
  155. X/*
  156. X**  Print syntax message and die.
  157. X*/
  158. void
  159. SynErr(text)
  160. X    char    *text;
  161. X{
  162. X    Note("Fatal syntax error in %s statement.\n", text);
  163. X    exit(1);
  164. X}
  165. X
  166. X/**
  167. X***        I N P U T   R O U T I N E S
  168. X**/
  169. X
  170. X
  171. X/*
  172. X**  Miniscule regular-expression matcher; only groks the . meta-character.
  173. X*/
  174. static int
  175. Matches(p, text)
  176. X    REGISTER char    *p;
  177. X    REGISTER char    *text;
  178. X{
  179. X    for (; *p && *text; text++, p++)
  180. X    if (*p != *text && *p != '.')
  181. X        return(FALSE);
  182. X    return(TRUE);
  183. X}
  184. X
  185. X
  186. X
  187. X/*
  188. X**  Read input, possibly handling escaped returns.  Returns a value so
  189. X**  we can do things like "while (GetLine(TRUE))", which is a hack.  This
  190. X**  should also be split into two separate routines, and punt the Flag
  191. X**  argument, but so it goes.
  192. X*/
  193. int
  194. GetLine(Flag)
  195. X    REGISTER int     Flag;
  196. X{
  197. X    REGISTER char    *p;
  198. X    REGISTER char    *q;
  199. X    char         buf[LINE_SIZE];
  200. X
  201. X    if (Interactive) {
  202. X    Fprintf(stderr, "Line %d%s>  ", LineNum, Running ? "" : "(SKIP)");
  203. X    (void)fflush(stderr);
  204. X    }
  205. X    Text[0] = '\0';
  206. X    for (q = Text; fgets(buf, sizeof buf, Input); q += strlen(strcpy(q, buf))) {
  207. X    LineNum++;
  208. X    p = &buf[strlen(buf) - 1];
  209. X    if (*p != '\n') {
  210. X        Note("Input line too long.\n", (char *)NULL);
  211. X        exit(1);
  212. X    }
  213. X    if (!Flag || p == buf || p[-1] != '\\') {
  214. X        (void)strcpy(q, buf);
  215. X        return(1);
  216. X    }
  217. X    p[-1] = '\0';
  218. X    if (Interactive) {
  219. X        Fprintf(stderr, "PS2>  ");
  220. X        (void)fflush(stderr);
  221. X    }
  222. X    }
  223. X    Note("RAN OUT OF INPUT.\n", (char *)NULL);
  224. X    exit(1);
  225. X    /* NOTREACHED */
  226. X}
  227. X
  228. X
  229. X/*
  230. X**  Copy a sub-string of characters into dynamic space.
  231. X*/
  232. static char *
  233. CopyRange(Start, End)
  234. X    char    *Start;
  235. X    char    *End;
  236. X{
  237. X    char    *p;
  238. X    int         i;
  239. X
  240. X    i = End - Start + 1;
  241. X    p = strncpy(NEW(char, i + 1), Start, i);
  242. X    p[i] = '\0';
  243. X    return(p);
  244. X}
  245. X
  246. X
  247. X/*
  248. X**  Split a line up into shell-style "words."
  249. X*/
  250. int
  251. Argify(ArgV)
  252. X    char        **ArgV;
  253. X{
  254. X    REGISTER char    **av;
  255. X    REGISTER char     *p;
  256. X    REGISTER char     *q;
  257. X    
  258. X    for (av = ArgV, p = Text; *p; p++) {
  259. X    /* Skip whitespace, but treat "\ " as a letter. */
  260. X    for (; ISwhit(*p); p++)
  261. X        if (ISmeta(*p))
  262. X        p++;
  263. X    if (ISterm(*p))
  264. X        break;
  265. X    switch (SynTable[*p]) {
  266. X    default:
  267. X        Note("Bad case %x in Argify.\n", (char *)SynTable[*p]);
  268. X        /* FALLTHROUGH */
  269. X    case C_META:
  270. X        p++;
  271. X        /* FALLTHROUGH */
  272. X    case C_WHIT:
  273. X    case C_LETR:
  274. X        for (q = p; ISletr(*++q) || ISmeta(q[-1]); )
  275. X        ;
  276. X        *av++ = CopyRange(p, --q);
  277. X        p = q;
  278. X        break;
  279. X    case C_DUBL:
  280. X        if (*p == p[1]) {
  281. X        *av++ = CopyRange(p, p + 1);
  282. X        p++;
  283. X        break;
  284. X        }
  285. X        /* FALLTHROUGH */
  286. X    case C_WORD:
  287. X        *av++ = CopyRange(p, p);
  288. X        break;
  289. X    case C_QUOT:
  290. X        for (q = p; *++q; )
  291. X        if (*q == *p && !ISmeta(q[-1]))
  292. X            break;
  293. X        *av++ = CopyRange(p + 1, q - 1);
  294. X        p = q;
  295. X        break;
  296. X    }
  297. X    }
  298. X    *av = NULL;
  299. X    if (av > &ArgV[MAX_WORDS - 1])
  300. X    SynErr("TOO MANY WORDS IN LINE");
  301. X    return(av - ArgV);
  302. X}
  303. X
  304. X/**
  305. X***        V A R I A B L E   R O U T I N E S
  306. X**/
  307. X
  308. X
  309. X/*
  310. X**  Return the value of a variable, or an empty string.
  311. X*/
  312. static char *
  313. GetVar(Name)
  314. X    REGISTER char    *Name;
  315. X{
  316. X    REGISTER VAR    *Vptr;
  317. X
  318. X    for (Vptr = VarList; Vptr < &VarList[MAX_VARS]; Vptr++)
  319. X    if (EQ(Vptr->Name, Name))
  320. X        return(Vptr->Value);
  321. X
  322. X    /* Try the environment. */
  323. X    return((Name = getenv(Name)) ? Name : "");
  324. X}
  325. X
  326. X
  327. X/*
  328. X**  Insert a variable/value pair into the list of variables.
  329. X*/
  330. void
  331. SetVar(Name, Value)
  332. X    REGISTER char    *Name;
  333. X    REGISTER char    *Value;
  334. X{
  335. X    REGISTER VAR    *Vptr;
  336. X    REGISTER VAR    *FreeVar;
  337. X
  338. X    /* Skip leading whitespace in variable names, sorry... */
  339. X    while (ISwhit(*Name))
  340. X    Name++;
  341. X
  342. X    /* Try to find the variable in the table. */
  343. X    for (Vptr = VarList, FreeVar = NULL; Vptr < &VarList[MAX_VARS]; Vptr++)
  344. X    if (Vptr->Name) {
  345. X        if (EQ(Vptr->Name, Name)) {
  346. X        free(Vptr->Value);
  347. X        Vptr->Value = COPY(Value);
  348. X        return;
  349. X        }
  350. X    }
  351. X    else if (FreeVar == NULL)
  352. X        FreeVar = Vptr;
  353. X
  354. X    if (FreeVar == NULL) {
  355. X    Fprintf(stderr, "Overflow, can't do '%s=%s'\n", Name, Value);
  356. X    SynErr("ASSIGNMENT");
  357. X    }
  358. X    FreeVar->Name = COPY(Name);
  359. X    FreeVar->Value = COPY(Value);
  360. X}
  361. X
  362. X
  363. X/*
  364. X**  Expand variable references inside a word that are of the form:
  365. X**    foo${var}bar
  366. X**    foo$$bar
  367. X**  Returns a pointer to a static area which is overwritten every
  368. X**  other time it is called, so that we can do EQ(Expand(a), Expand(b)).
  369. X*/
  370. static char *
  371. XExpand(p)
  372. X    REGISTER char    *p;
  373. X{
  374. X    static char         buff[2][VAR_VALUE_SIZE];
  375. X    static int         Flag;
  376. X    REGISTER char    *q;
  377. X    REGISTER char    *n;
  378. X    REGISTER char     Closer;
  379. X    char         name[VAR_NAME_SIZE];
  380. X
  381. X    /* This is a hack, but it makes things easier in DoTEST, q.v. */
  382. X    if (p == NULL)
  383. X    return(p);
  384. X
  385. X    /* Pick the "other" buffer then loop over the string to be expanded. */
  386. X    for (Flag = 1 - Flag, q = buff[Flag]; *p; )
  387. X    if (*p == '$')
  388. X        if (*++p == '$') {
  389. X        (void)sprintf(name, "%d", Pid());
  390. X        q += strlen(strcpy(q, name));
  391. X        p++;
  392. X        }
  393. X        else if (*p == '?') {
  394. X        /* Fake it -- all commands always succeed, here. */
  395. X        *q++ = '0';
  396. X        *q = '\0';
  397. X        p++;
  398. X        }
  399. X        else {
  400. X        Closer =  (*p == '{') ? *p++ : '\0';
  401. X        for (n = name; *p && *p != Closer; )
  402. X            *n++ = *p++;
  403. X        if (*p)
  404. X            p++;
  405. X        *n = '\0';
  406. X        q += strlen(strcpy(q, GetVar(name)));
  407. X        }
  408. X    else
  409. X        *q++ = *p++;
  410. X    *q = '\0';
  411. X    return(buff[Flag]);
  412. X}
  413. X
  414. X
  415. X/*
  416. X**  Do a variable assignment of the form:
  417. X**    var=value
  418. X**    var="quoted value"
  419. X**    var="...${var}..."
  420. X**    etc.
  421. X*/
  422. static void
  423. DoASSIGN(Name)
  424. X    REGISTER char    *Name;
  425. X{
  426. X    REGISTER char    *Value;
  427. X    REGISTER char    *q;
  428. X    REGISTER char     Quote;
  429. X
  430. X    /* Split out into name:value strings, and deal with quoted values. */
  431. X    Value = IDX(Name, '=');
  432. X    *Value = '\0';
  433. X    if (ISquot(*++Value))
  434. X    for (Quote = *Value++, q = Value; *++q && *q != Quote; )
  435. X        ;
  436. X    else
  437. X    for (q = Value; ISletr(*q); q++)
  438. X        ;
  439. X    *q = '\0';
  440. X
  441. X    SetVar(Name, Expand(Value));
  442. X}
  443. X
  444. X/**
  445. X***        " O U T P U T "   C O M M A N D S
  446. X**/
  447. X
  448. X
  449. X/*
  450. X**  Do a cat command.  Understands the following:
  451. X**    cat >arg1 <<arg2
  452. X**    cat >>arg1 <<arg2
  453. X**    cat >>arg1 /dev/null
  454. X**  Except that arg2 is assumed to be quoted -- i.e., no expansion of meta-chars
  455. X**  inside the "here" document is done.  The IO redirection can be in any order.
  456. X*/
  457. X/* ARGSUSED */
  458. static int
  459. DoCAT(ac, av)
  460. X    int             ac;
  461. X    REGISTER char    *av[];
  462. X{
  463. X    REGISTER FILE    *Out;
  464. X    REGISTER char    *Ending;
  465. X    REGISTER char    *Source;
  466. X    REGISTER int     V;
  467. X    REGISTER int     l;
  468. X
  469. X    /* Parse the I/O redirecions. */
  470. X    for (V = TRUE, Source = NULL, Out = NULL, Ending = NULL; *++av; )
  471. X    if (EQ(*av, ">") && av[1]) {
  472. X        av++;
  473. X        /* This is a hack, but maybe MS-DOS doesn't have /dev/null? */
  474. X        Out = Running ? fopen(Expand(*av), "w") : stderr;
  475. X    }
  476. X    else if (EQ(*av, ">>") && av[1]) {
  477. X        av++;
  478. X        /* And besides, things are actually faster this way. */
  479. X        Out = Running ? fopen(Expand(*av), "a") : stderr;
  480. X    }
  481. X    else if (EQ(*av, "<<") && av[1]) {
  482. X        for (Ending = *++av; *Ending == '\\'; Ending++)
  483. X        ;
  484. X        l = strlen(Ending);
  485. X    }
  486. X    else if (!EQ(Source = *av, "/dev/null"))
  487. X        SynErr("CAT (bad input filename)");
  488. X
  489. X    if (Out == NULL || (Ending == NULL && Source == NULL)) {
  490. X    Note("Missing parameter in CAT command.\n", (char *)NULL);
  491. X    V = FALSE;
  492. X    }
  493. X
  494. X    /* Read the input, spit it out. */
  495. X    if (V && Running && Out != stderr) {
  496. X    if (Source == NULL)
  497. X        while (GetLine(FALSE) && !EQn(Text, Ending, l))
  498. X        (void)fputs(Text, Out);
  499. X    (void)fclose(Out);
  500. X    }
  501. X    else
  502. X    while (GetLine(FALSE) && !EQn(Text, Ending, l))
  503. X        ;
  504. X
  505. X    return(V);
  506. X}
  507. X
  508. X
  509. X/*
  510. X**  Do a SED command.  Understands the following:
  511. X**    sed sX^yyyyXX >arg1 <<arg2
  512. X**    sed -e sX^yyyyXX >arg1 <<arg2
  513. X**  Where the yyyy is a miniscule regular expression; see Matches(), above.
  514. X**  The "X" can be any single character and the ^ is optional (sigh).  No
  515. X**  shell expansion is done inside the "here' document.  The IO redirection
  516. X**  can be in any order.
  517. X*/
  518. X/* ARGSUSED */
  519. static int
  520. DoSED(ac, av)
  521. X    int             ac;
  522. X    REGISTER char    *av[];
  523. X{
  524. X    REGISTER FILE    *Out;
  525. X    REGISTER char    *Pattern;
  526. X    REGISTER char    *Ending;
  527. X    REGISTER char    *p;
  528. X    REGISTER int     V;
  529. X    REGISTER int     l;
  530. X    REGISTER int     i;
  531. X
  532. X    /* Parse IO redirection stuff. */
  533. X    for (V = TRUE, Out = NULL, Pattern = NULL, Ending = NULL; *++av; )
  534. X    if (EQ(*av, ">") && av[1]) {
  535. X        av++;
  536. X        Out = Running ? fopen(Expand(*av), "w") : stderr;
  537. X    }
  538. X    else if (EQ(*av, ">>") && av[1]) {
  539. X        av++;
  540. X        Out = Running ? fopen(Expand(*av), "a") : stderr;
  541. X    }
  542. X    else if (EQ(*av, "<<") && av[1]) {
  543. X        for (Ending = *++av; *Ending == '\\'; Ending++)
  544. X        ;
  545. X        l = strlen(Ending);
  546. X    }
  547. X    else
  548. X        Pattern = EQ(*av, "-e") && av[1] ? *++av : *av;
  549. X
  550. X    /* All there? */
  551. X    if (Out == NULL || Ending == NULL || Pattern == NULL) {
  552. X    Note("Missing parameter in SED command.\n", (char *)NULL);
  553. X    V = FALSE;
  554. X    }
  555. X
  556. X    /* Parse the substitute command and its pattern. */
  557. X    if (*Pattern != 's') {
  558. X    Note("Bad SED command -- not a substitute.\n", (char *)NULL);
  559. X    V = FALSE;
  560. X    }
  561. X    else {
  562. X    Pattern++;
  563. X    p = Pattern + strlen(Pattern) - 1;
  564. X    if (*p != *Pattern || *--p != *Pattern) {
  565. X        Note("Bad substitute pattern in SED command.\n", (char *)NULL);
  566. X        V = FALSE;
  567. X    }
  568. X    else {
  569. X        /* Now check the pattern. */
  570. X        if (*++Pattern == '^')
  571. X        Pattern++;
  572. X        for (*p = '\0', i = strlen(Pattern), p = Pattern; *p; p++)
  573. X        if (*p == '[' || *p == '*' || *p == '$') {
  574. X            Note("Bad meta-character in SED pattern.\n", (char *)NULL);
  575. X            V = FALSE;
  576. X        }
  577. X    }
  578. X    }
  579. X
  580. X    /* Spit out the input. */
  581. X    if (V && Running && Out != stderr) {
  582. X    while (GetLine(FALSE) && !EQn(Text, Ending, l))
  583. X        (void)fputs(Matches(Pattern, Text) ? &Text[i] : Text, Out);
  584. X    (void)fclose(Out);
  585. X    }
  586. X    else
  587. X    while (GetLine(FALSE) && !EQn(Text, Ending, l))
  588. X        ;
  589. X
  590. X    return(V);
  591. X}
  592. X
  593. X/**
  594. X***        " S I M P L E "   C O M M A N D S
  595. X**/
  596. X
  597. X
  598. X/*
  599. X**  Parse a cp command of the form:
  600. X**    cp /dev/null arg
  601. X**  We should check if "arg" is a safe file to clobber, but...
  602. X*/
  603. static int
  604. DoCP(ac, av)
  605. X    int         ac;
  606. X    char    *av[];
  607. X{
  608. X    FILE    *F;
  609. X
  610. X    if (Running) {
  611. X    if (ac != 3 || !EQ(av[1], "/dev/null"))
  612. X        SynErr("CP");
  613. X    if (F = fopen(Expand(av[2]), "w")) {
  614. X        (void)fclose(F);
  615. X        return(TRUE);
  616. X    }
  617. X    Note("Can't create %s.\n", av[2]);
  618. X    }
  619. X    return(FALSE);
  620. X}
  621. X
  622. X
  623. X/*
  624. X**  Do a mkdir command of the form:
  625. X**    mkdir arg
  626. X*/
  627. static int
  628. DoMKDIR(ac, av)
  629. X    int         ac;
  630. X    char    *av[];
  631. X{
  632. X    if (Running) {
  633. X    if (ac != 2)
  634. X        SynErr("MKDIR");
  635. X    if (mkdir(Expand(av[1]), 0777) >= 0)
  636. X        return(TRUE);
  637. X    Note("Can't make directory %s.\n", av[1]);
  638. X    }
  639. X    return(FALSE);
  640. X}
  641. X
  642. X
  643. X/*
  644. X**  Do a cd command of the form:
  645. X**    cd arg
  646. X**    chdir arg
  647. X*/
  648. static int
  649. DoCD(ac, av)
  650. X    int         ac;
  651. X    char    *av[];
  652. X{
  653. X    if (Running) {
  654. X    if (ac != 2)
  655. X        SynErr("CD");
  656. X    if (chdir(Expand(av[1])) >= 0)
  657. X        return(TRUE);
  658. X    Note("Can't cd to %s.\n", av[1]);
  659. X    }
  660. X    return(FALSE);
  661. X}
  662. X
  663. X
  664. X/*
  665. X**  Do the echo command.  Understands the "-n" hack.
  666. X*/
  667. X/* ARGSUSED */
  668. static int
  669. DoECHO(ac, av)
  670. X    int         ac;
  671. X    char    *av[];
  672. X{
  673. X    int         Flag;
  674. X
  675. X    if (Running) {
  676. X    if (Flag = av[1] != NULL && EQ(av[1], "-n"))
  677. X        av++;
  678. X    while (*++av)
  679. X        Fprintf(stderr, "%s ", Expand(*av));
  680. X    if (!Flag)
  681. X        Fprintf(stderr, "\n");
  682. X    (void)fflush(stderr);
  683. X    }
  684. X    return(TRUE);
  685. X}
  686. X
  687. X
  688. X/*
  689. X**  Generic "handler" for commands we can't do.
  690. X*/
  691. static int
  692. DoIT(ac, av)
  693. X    int         ac;
  694. X    char    *av[];
  695. X{
  696. X    if (Running)
  697. X    Fprintf(stderr, "You'll have to do this yourself:\n\t%s ", *av);
  698. X    return(DoECHO(ac, av));
  699. X}
  700. X
  701. X
  702. X/*
  703. X**  Do an EXIT command.
  704. X*/
  705. static int
  706. DoEXIT(ac, av)
  707. X    int         ac;
  708. X    char    *av[];
  709. X{
  710. X    ac = *++av ? atoi(Expand(*av)) : 0;
  711. X    Fprintf(stderr, "Exiting, with status %d\n", ac);
  712. X#ifdef    MSDOS
  713. X    longjmp(jEnv, 1);
  714. X#endif    /* MSDOS */
  715. X    return(ac);
  716. X}
  717. X
  718. X
  719. X/*
  720. X**  Do an EXPORT command.  Often used to make sure the archive is being
  721. X**  unpacked with the Bourne (or Korn?) shell.  We look for:
  722. X**    export PATH blah blah blah
  723. X*/
  724. static int
  725. DoEXPORT(ac, av)
  726. X    int         ac;
  727. X    char    *av[];
  728. X{
  729. X    if (ac < 2 || !EQ(av[1], "PATH"))
  730. X    SynErr("EXPORT");
  731. X    return(TRUE);
  732. X}
  733. X
  734. X/**
  735. X***        F L O W - O F - C O N T R O L   C O M M A N D S
  736. X**/
  737. X
  738. X
  739. X/*
  740. X**  Parse a "test" statement.  Returns TRUE or FALSE.  Understands the
  741. X**  following tests:
  742. X**    test {!} -f arg        Is arg {not} a plain file?
  743. X**    test {!} -d arg        Is arg {not} a directory?
  744. X**    test {!} $var -eq $var    Is the variable {not} equal to the variable?
  745. X**    test {!} $var != $var    Is the variable {not} equal to the variable?
  746. X**    test {!} ddd -ne `wc -c {<} arg`
  747. X**                Is size of arg {not} equal to ddd in bytes?
  748. X**    test -f arg -a $var -eq val
  749. X**                Used by my shar, check for file clobbering
  750. X**  These last two tests are starting to really push the limits of what is
  751. X**  reasonable to hard-code, but they are common cliches in shell archive
  752. X**  "programming."  We also understand the [ .... ] way of writing test.
  753. X**  If we can't parse the test, we show the command and ask the luser.
  754. X*/
  755. static int
  756. DoTEST(ac, av)
  757. X    REGISTER int      ac;
  758. X    REGISTER char     *av[];
  759. X{
  760. X    REGISTER char    **p;
  761. X    REGISTER char     *Name;
  762. X    REGISTER FILE     *DEVTTY;
  763. X    REGISTER int      V;
  764. X    REGISTER int      i;
  765. X    char          buff[LINE_SIZE];
  766. X
  767. X    /* Quick test. */
  768. X    if (!Running)
  769. X    return(FALSE);
  770. X
  771. X    /* See if we're called as "[ ..... ]" */
  772. X    if (EQ(*av, "[")) {
  773. X    for (i = 1; av[i] && !EQ(av[i], "]"); i++)
  774. X        ;
  775. X    free(av[i]);
  776. X    av[i] = NULL;
  777. X    ac--;
  778. X    }
  779. X
  780. X    /* Ignore the "test" argument. */
  781. X    av++;
  782. X    ac--;
  783. X
  784. X    /* Inverted test? */
  785. X    if (EQ(*av, "!")) {
  786. X    V = FALSE;
  787. X    av++;
  788. X    ac--;
  789. X    }
  790. X    else
  791. X    V = TRUE;
  792. X
  793. X    /* Testing for file-ness? */
  794. X    if (ac == 2 && EQ(av[0], "-f") && (Name = Expand(av[1])))
  795. X    return(GetStat(Name) && Ftype(Name) == F_FILE ? V : !V);
  796. X
  797. X    /* Testing for directory-ness? */
  798. X    if (ac == 2 && EQ(av[0], "-d") && (Name = Expand(av[1])))
  799. X    return(GetStat(Name) && Ftype(Name) == F_DIR ? V : !V);
  800. X
  801. X    /* Testing a variable's value? */
  802. X    if (ac == 3 && (EQ(av[1], "-eq") || EQ(av[1], "=")))
  803. X    return(EQ(Expand(av[0]), Expand(av[2])) ? V : !V);
  804. X    if (ac == 3 && (EQ(av[1], "-ne") || EQ(av[1], "!=")))
  805. X    return(!EQ(Expand(av[0]), Expand(av[2])) ? V : !V);
  806. X
  807. X    /* Testing a file's size? */
  808. X    if (ac == (av[5] && EQ(av[5], "<") ? 8 : 7) && isdigit(av[0][0])
  809. X     && (EQ(av[1], "-ne") || EQ(av[1], "-eq"))
  810. X     && EQ(av[2], "`") && EQ(av[3], "wc")
  811. X     && EQ(av[4], "-c") && EQ(av[ac - 1], "`")) {
  812. X    if (GetStat(av[ac - 2])) {
  813. X        if (EQ(av[1], "-ne"))
  814. X        return(Fsize(av[ac - 2]) != atol(av[0]) ? V : !V);
  815. X        return(Fsize(av[ac - 2]) == atol(av[0]) ? V : !V);
  816. X    }
  817. X    Note("Can't get status of %s.\n", av[ac - 2]);
  818. X    }
  819. X
  820. X    /* Testing for existing, but can clobber? */
  821. X    if (ac == 6 && EQ(av[0], "-f") && EQ(av[2], "-a")
  822. X     && (EQ(av[4], "!=") || EQ(av[4], "-ne")))
  823. X    return(GetStat(Name = Expand(av[1])) && Ftype(Name) == F_FILE
  824. X           && EQ(Expand(av[3]), Expand(av[5])) ? !V : V);
  825. X
  826. X    /* I give up -- print it out, and let's ask Mikey, he can do it... */
  827. X    Fprintf(stderr, "Can't parse this test:\n\t");
  828. X    for (i = FALSE, p = av; *p; p++) {
  829. X    Fprintf(stderr, "%s ", *p);
  830. X    if (p[0][0] == '$')
  831. X        i = TRUE;
  832. X    }
  833. X    if (i) {
  834. X    Fprintf(stderr, "\n(Here it is with shell variables expanded...)\n\t");
  835. X    for (p = av; *p; p++)
  836. X        Fprintf(stderr, "%s ", Expand(*p));
  837. X    }
  838. X    Fprintf(stderr, "\n");
  839. X
  840. X    DEVTTY = fopen(THE_TTY, "r");
  841. X    do {
  842. X    Fprintf(stderr, "Is value true/false/quit [tfq] (q):  ");
  843. X    (void)fflush(stderr);
  844. X    clearerr(DEVTTY);
  845. X    if (fgets(buff, sizeof buff, DEVTTY) == NULL
  846. X     || buff[0] == 'q' || buff[0] == 'Q' || buff[0] == '\n')
  847. X        SynErr("TEST");
  848. X    if (buff[0] == 't' || buff[0] == 'T') {
  849. X        (void)fclose(DEVTTY);
  850. X        return(TRUE);
  851. X    }
  852. X    } while (buff[0] != 'f' && buff[0] != 'F');
  853. X    (void)fclose(DEVTTY);
  854. X    return(FALSE);
  855. X}
  856. X
  857. X
  858. X/*
  859. X**  Do an IF statement.
  860. X*/
  861. static int
  862. DoIF(ac, av)
  863. X    REGISTER int     ac;
  864. X    REGISTER char    *av[];
  865. X{
  866. X    REGISTER char    **p;
  867. X    REGISTER int      Flag;
  868. X    char         *vec[MAX_WORDS];
  869. X    char        **Pushed;
  870. X
  871. X    /* Skip first argument. */
  872. X    if (!EQ(*++av, "[") && !EQ(*av, "test"))
  873. X    SynErr("IF");
  874. X    ac--;
  875. X
  876. X    /* Look for " ; then " on this line, or "then" on next line. */
  877. X    for (Pushed = NULL, p = av; *p; p++)
  878. X    if (Flag = EQ(*p, ";")) {
  879. X        if (p[1] == NULL || !EQ(p[1], "then"))
  880. X        SynErr("IF");
  881. X        *p = NULL;
  882. X        ac -= 2;
  883. X        break;
  884. X    }
  885. X    if (!Flag) {
  886. X    (void)GetLine(TRUE);
  887. X    if (Argify(vec) > 1)
  888. X        Pushed = &vec[1];
  889. X    if (!EQ(vec[0], "then"))
  890. X        SynErr("IF (missing THEN)");
  891. X    }
  892. X
  893. X    if (DoTEST(ac, av)) {
  894. X    if (Pushed)
  895. X        (void)Exec(Pushed);
  896. X    while (GetLine(TRUE)) {
  897. X        if ((ac = Argify(vec)) == 1 && EQ(vec[0], "fi"))
  898. X        break;
  899. X        if (EQ(vec[0], "else")) {
  900. X        DoUntil("fi", FALSE);
  901. X        break;
  902. X        }
  903. X        (void)Exec(vec);
  904. X    }
  905. X    }
  906. X    else
  907. X    while (GetLine(TRUE)) {
  908. X        if ((ac = Argify(vec)) == 1 && EQ(vec[0], "fi"))
  909. X        break;
  910. X        if (EQ(vec[0], "else")) {
  911. X        if (ac > 1)
  912. X            (void)Exec(&vec[1]);
  913. X        DoUntil("fi", Running);
  914. X        break;
  915. X        }
  916. X    }
  917. X    return(TRUE);
  918. X}
  919. X
  920. X
  921. X/*
  922. X**  Do a FOR statement.
  923. X*/
  924. static int
  925. DoFOR(ac, av)
  926. X    REGISTER int      ac;
  927. X    REGISTER char     *av[];
  928. X{
  929. X    REGISTER char     *Var;
  930. X    REGISTER char    **Values;
  931. X    REGISTER int      Found;
  932. X    long          Here;
  933. X    char         *vec[MAX_WORDS];
  934. X
  935. X    /* Check usage, get variable name and eat noise words. */
  936. X    if (ac < 4 || !EQ(av[2], "in"))
  937. X    SynErr("FOR");
  938. X    Var = av[1];
  939. X    ac -= 3;
  940. X    av += 3;
  941. X
  942. X    /* Look for "; do" on this line, or just "do" on next line. */
  943. X    for (Values = av; *++av; )
  944. X    if (Found = EQ(*av, ";")) {
  945. X        if (av[1] == NULL || !EQ(av[1], "do"))
  946. X        SynErr("FOR");
  947. X        *av = NULL;
  948. X        break;
  949. X    }
  950. X    if (!Found) {
  951. X    (void)GetLine(TRUE);
  952. X    if (Argify(vec) != 1 || !EQ(vec[0], "do"))
  953. X        SynErr("FOR (missing DO)");
  954. X    }
  955. X
  956. X    for (Here = ftell(Input); *Values; ) {
  957. X    SetVar(Var, *Values);
  958. X    DoUntil("done", Running);
  959. X        ;
  960. X    /* If we're not Running, only go through the loop once. */
  961. X    if (!Running)
  962. X        break;
  963. X    if (*++Values && (fseek(Input, Here, 0) < 0 || ftell(Input) != Here))
  964. X        SynErr("FOR (can't seek back)");
  965. X    }
  966. X
  967. X    return(TRUE);
  968. X}
  969. X
  970. X
  971. X/*
  972. X**  Do a CASE statement of the form:
  973. X**    case $var in
  974. X**        text1)
  975. X**        ...
  976. X**        ;;
  977. X**    esac
  978. X**  Where text1 is a simple word or an asterisk.
  979. X*/
  980. static int
  981. DoCASE(ac, av)
  982. X    REGISTER int     ac;
  983. X    REGISTER char    *av[];
  984. X{
  985. X    REGISTER int     FoundIt;
  986. X    char        *vec[MAX_WORDS];
  987. X    char         Value[VAR_VALUE_SIZE];
  988. X
  989. X    if (ac != 3 || !EQ(av[2], "in"))
  990. X    SynErr("CASE");
  991. X    (void)strcpy(Value, Expand(av[1]));
  992. X
  993. X    for (FoundIt = FALSE; GetLine(TRUE); ) {
  994. X    ac = Argify(vec);
  995. X    if (EQ(vec[0], "esac"))
  996. X        break;
  997. X    /* This is for vi: (-; sigh. */
  998. X    if (ac != 2 || !EQ(vec[1], ")"))
  999. X        SynErr("CASE");
  1000. X    if (!FoundIt && (EQ(vec[0], Value) || EQ(vec[0], "*"))) {
  1001. X        FoundIt = TRUE;
  1002. X        if (Running && ac > 2)
  1003. X        (void)Exec(&vec[2]);
  1004. X        DoUntil(";;", Running);
  1005. X    }
  1006. X    else
  1007. X        DoUntil(";;", FALSE);
  1008. X    }
  1009. X    return(TRUE);
  1010. X}
  1011. X
  1012. X
  1013. X
  1014. X/*
  1015. X**  Dispatch table of known commands.
  1016. X*/
  1017. static COMTAB     Dispatch[] = {
  1018. X    {    "cat",        DoCAT        },
  1019. X    {    "case",        DoCASE        },
  1020. X    {    "cd",        DoCD        },
  1021. X    {    "chdir",    DoCD        },
  1022. X    {    "chmod",    DoIT        },
  1023. X    {    "cp",        DoCP        },
  1024. X    {    "echo",        DoECHO        },
  1025. X    {    "exit",        DoEXIT        },
  1026. X    {    "export",    DoEXPORT    },
  1027. X    {    "for",        DoFOR        },
  1028. X    {    "if",        DoIF        },
  1029. X    {    "mkdir",    DoMKDIR        },
  1030. X    {    "rm",        DoIT        },
  1031. X    {    "sed",        DoSED        },
  1032. X    {    "test",        DoTEST        },
  1033. X    {    "[",        DoTEST        },
  1034. X    {    ":",        DoIT        },
  1035. X    {    "",        NULL        }
  1036. X};
  1037. X
  1038. X
  1039. X/*
  1040. X**  Dispatch on a parsed line.
  1041. X*/
  1042. int
  1043. XExec(av)
  1044. X    REGISTER char    *av[];
  1045. X{
  1046. X    REGISTER int     i;
  1047. X    REGISTER COMTAB    *p;
  1048. X
  1049. X    /* We have to re-calculate this because our callers can't always
  1050. X       pass the count down to us easily. */
  1051. X    for (i = 0; av[i]; i++)
  1052. X    ;
  1053. X    if (i) {
  1054. X    /* Is this a command we know? */
  1055. X    for (p = Dispatch; p->Func; p++)
  1056. X        if (EQ(av[0], p->Name)) {
  1057. X        i = (*p->Func)(i, av);
  1058. X        if (p->Func == DoEXIT)
  1059. X            /* Sigh; this is a hack. */
  1060. X            return(-FALSE);
  1061. X        break;
  1062. X        }
  1063. X
  1064. X    /* If not a command, try it as a variable assignment. */
  1065. X    if (p->Func == NULL)
  1066. X        /* Yes, we look for "=" in the first word, but pass down
  1067. X           the whole line. */
  1068. X        if (IDX(av[0], '='))
  1069. X        DoASSIGN(Text);
  1070. X        else
  1071. X        Note("Command %s unknown.\n", av[0]);
  1072. X
  1073. X    /* Free the line. */
  1074. X    for (i = 0; av[i]; i++)
  1075. X        free(av[i]);
  1076. X    }
  1077. X    return(TRUE);
  1078. X}
  1079. X
  1080. X
  1081. X/*
  1082. X**  Do until we reach a specific terminator.
  1083. X*/
  1084. static
  1085. DoUntil(Terminator, NewVal)
  1086. X    char    *Terminator;
  1087. X    int         NewVal;
  1088. X{
  1089. X    char    *av[MAX_WORDS];
  1090. X    int         OldVal;
  1091. X
  1092. X    for (OldVal = Running, Running = NewVal; GetLine(TRUE); )
  1093. X    if (Argify(av)) {
  1094. X        if (EQ(av[0], Terminator))
  1095. X        break;
  1096. X        (void)Exec(av);
  1097. X    }
  1098. X
  1099. X    Running = OldVal;
  1100. X}
  1101. END_OF_FILE
  1102. if test 24206 -ne `wc -c <'parser.c'`; then
  1103.     echo shar: \"'parser.c'\" unpacked with wrong size!
  1104. fi
  1105. # end of 'parser.c'
  1106. fi
  1107. echo shar: End of archive 3 \(of 3\).
  1108. cp /dev/null ark3isdone
  1109. MISSING=""
  1110. for I in 1 2 3 ; do
  1111.     if test ! -f ark${I}isdone ; then
  1112.     MISSING="${MISSING} ${I}"
  1113.     fi
  1114. done
  1115. if test "${MISSING}" = "" ; then
  1116.     echo You have unpacked all 3 archives.
  1117.     echo "See the README"
  1118.     rm -f ark[1-9]isdone
  1119. else
  1120.     echo You still need to unpack the following archives:
  1121.     echo "        " ${MISSING}
  1122. fi
  1123. ##  End of shell archive.
  1124. exit 0
  1125.